package net.plentz.tagger.ui.widgets;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.plentz.tagger.data.MusicData;
import net.plentz.tagger.data.access.FileDAO;
import net.plentz.tagger.data.access.MusicDAO;
import net.plentz.tagger.ui.listeners.events.AbstractEvent;
import net.plentz.tagger.ui.listeners.events.AbstractEvent.Action;
import net.plentz.tagger.ui.listeners.events.MusicDataEvent;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

public class MusicListWidget extends AbstractWidget {

	private static final String TreeNodeFilePathDataKey = "TreeNodeFilePath";

	public static enum Column {

		FILENAME("Filename", 256),
		FILESIZE("Size", 64);

		private String label;
		private int width;

		private Column(String label, int width) {
			this.label = label;
			this.width = width;
		}

		public String getLabel() {
			return this.label;
		}

		public int getWidth() {
			return this.width;
		}

	}

	private Column[] columns;
	private Tree fileTree;
	private Map<String, TreeItem> fileTreeItemMap;

	public MusicListWidget() {
		this.fileTreeItemMap = new HashMap<String, TreeItem>();
	}

	@Override
	public Composite render(Composite parent, int flags) {

		// Append the tree
		Composite c = new Composite(parent, flags);
		c.setLayout(new FillLayout());

		this.fileTree = new Tree(c, SWT.BORDER | SWT.MULTI);
		this.fileTree.setHeaderVisible(true);

		if (this.columns == null) {
			throw new RuntimeException("MusicListWidget needs columns");
		}

		for (Column column : this.columns) {
			TreeColumn treeColumn = new TreeColumn(this.fileTree, SWT.LEFT);
			treeColumn.setText(column.getLabel());
			treeColumn.setWidth(column.getWidth());
		}

		this.fileTree.addMouseListener(new DirectoryMouseListener());

		return c;

	}

	public void setColumns(Column[] columns) {
		if (this.columns != null) {
			// TODO Do something to dispose/recreate
		}
		this.columns = columns;
	}

	public void setFiles(List<File> files) {
		this.setFiles(files, null);
	}

	public void setFiles(List<File> files, String parentDirectory) {

		TreeItem parentTreeItem = null;
		String[] columnValues = new String[this.columns.length];

		if (parentDirectory == null) {
			// No parent, clear it out and start over
			this.fileTree.removeAll();
			this.fileTreeItemMap.clear();
		} else {
			// Parent exists, append to it
			parentTreeItem = this.fileTreeItemMap.get(parentDirectory);
			if (parentTreeItem.getItemCount() > 0) {
				// It's already populated. Unless we're refreshing, don't do
				// anything
				return;
			}
		}

		for (File file : files) {

			TreeItem treeItem = null;

			if (parentTreeItem != null) {
				treeItem = new TreeItem(parentTreeItem, SWT.NONE);
			} else {
				treeItem = new TreeItem(this.fileTree, SWT.NONE);
			}

			for (int i = 0, il = this.columns.length; i < il; i++) {
				switch (this.columns[i]) {
					case FILENAME:
						columnValues[i] = file.getName();
						break;
					case FILESIZE:
						columnValues[i] = file.isDirectory() ? "" : FileDAO.formatFileSize(file.length());
						break;
				}
			}

			try {
				treeItem.setText(columnValues);
				treeItem.setData(MusicListWidget.TreeNodeFilePathDataKey, file.getAbsolutePath());
				treeItem.setImage(new Image(treeItem.getDisplay(), FileDAO.getSystemIconForFile(file)));
			} catch (NoSuchAlgorithmException e) {
				throw new RuntimeException(e);
			}

			this.fileTreeItemMap.put(file.getAbsolutePath(), treeItem);
		}

		if (parentTreeItem != null) {
			parentTreeItem.setExpanded(true);
		}

	}

	private class DirectoryMouseListener implements MouseListener {

		@Override
		public void mouseDoubleClick(MouseEvent e) {
			TreeItem[] selections = MusicListWidget.this.fileTree.getSelection();
			for (TreeItem selection : selections) {
				File file = new File((String)selection.getData(MusicListWidget.TreeNodeFilePathDataKey));
				if (file.isDirectory()) {
					// If the node they doubleclicked is a directory,
					// repopulate/expand it
					MusicListWidget.this.setFiles(FileDAO.getFiles(file, true, new String[] {
						"mp3"
					}), file.getAbsolutePath());
				} else if (file.isFile()) {
					try {
						Desktop desktop = Desktop.getDesktop();
						desktop.open(file);
					} catch (IOException ex) {
						throw new RuntimeException(ex);
					}
				}
			}
		}

		@Override
		public void mouseDown(MouseEvent e) {
		}

		@Override
		public void mouseUp(MouseEvent e) {
			TreeItem[] selections = MusicListWidget.this.fileTree.getSelection();
			List<MusicData> selectedMusicData = new ArrayList<MusicData>();
			for (TreeItem selection : selections) {
				File file = new File((String)selection.getData(MusicListWidget.TreeNodeFilePathDataKey));
				if (!file.isDirectory()) {
					// If the selected node is a file
					try {
						selectedMusicData.add(MusicDAO.getMusicData(file));
					} catch (Exception ex) {
						throw new RuntimeException(ex);
					}
				}
			}
			if (selectedMusicData.size() > 0) {
				MusicListWidget.this.broadcastEvent(new MusicDataEvent(Action.SET_MUSIC_DATA, selectedMusicData));
			}
		}

	}

	@Override
	protected <T> void invokeEvent(AbstractEvent<T> event) {

		switch (event.getAction()) {
			case SET_GLOBAL_DIRECTORY:
				String directory = (String)event.getData();
				this.setFiles(FileDAO.getFiles(new File(directory), true, new String[] {
					"mp3"
				}));
				break;
		}

	}

}
